"""Weather service using QWeather (和风天气) API."""
import os
from typing import Tuple, Optional
import requests
import redis
import json
import logging
from datetime import datetime
from app.models.location import Location
from app.models.weather_data import WeatherData
from app.utils.error_handler import (
    retry_with_exponential_backoff,
    safe_api_call,
    NetworkError,
    APIError
)

logger = logging.getLogger(__name__)


class WeatherService:
    """Service for weather data and monitoring using QWeather API."""
    
    def __init__(self, qweather_key: str, redis_url: str, cache_duration: int = 1800):
        """Initialize weather service.
        
        Args:
            qweather_key: QWeather (和风天气) API key
            redis_url: Redis connection URL
            cache_duration: Cache duration in seconds (default 30 minutes)
        """
        self.qweather_key = qweather_key
        self.cache_duration = cache_duration
        self.redis_client = redis.from_url(redis_url, decode_responses=True)
        # 和风天气API端点
        # 免费版使用 devapi.qweather.com，商业版使用 api.qweather.com
        qweather_host = os.getenv('QWEATHER_HOST', 'api.qweather.com')
        self.qweather_now_url = f"https://{qweather_host}/v7/weather/now"
        self.qweather_air_url = f"https://{qweather_host}/v7/air/now"
    
    def _get_cache_key(self, location: Location) -> str:
        """Generate cache key for location.
        
        Args:
            location: Location
            
        Returns:
            Cache key string
        """
        # Round to 2 decimal places to group nearby locations
        lat = round(location.latitude, 2)
        lon = round(location.longitude, 2)
        return f"weather:{lat}:{lon}"
    
    def _get_session_weather_key(self, session_id: str) -> str:
        """Generate cache key for session weather.
        
        Args:
            session_id: Session ID
            
        Returns:
            Cache key string
        """
        return f"session_weather:{session_id}"

    @retry_with_exponential_backoff(max_retries=3, initial_delay=1.0)
    def get_current_weather(
        self,
        location: Location
    ) -> WeatherData:
        """Get current weather from QWeather API with Redis caching and retry.
        
        Args:
            location: Location to get weather for
            
        Returns:
            Current weather data
            
        Raises:
            NetworkError: If network request fails after retries
            APIError: If API returns error after retries
        """
        # Check cache first
        cache_key = self._get_cache_key(location)
        
        try:
            cached_data = self.redis_client.get(cache_key)
            if cached_data:
                data = json.loads(cached_data)
                logger.info(f"Using cached weather data for {cache_key}")
                return WeatherData(**data)
        except Exception as e:
            logger.warning(f"Failed to get cached weather data: {str(e)}")
        
        # Fetch from QWeather API
        params = {
            'key': self.qweather_key,
            'location': f"{location.longitude},{location.latitude}",
            'lang': 'zh'
        }
        
        try:
            response = safe_api_call(
                url=self.qweather_now_url,
                method='GET',
                params=params,
                timeout=10
            )
            
            result = response.json()
            
            # 和风天气返回 code "200" 表示成功
            if result.get('code') != '200':
                raise APIError(f"QWeather API error: code={result.get('code')}")
            
            now_data = result.get('now', {})
            
            # 获取空气质量数据（可选）
            aqi = None
            try:
                aqi = self._get_air_quality(location)
            except Exception as air_error:
                logger.warning(f"Failed to get air quality: {str(air_error)}")
            
            # 解析和风天气数据
            weather_data = WeatherData(
                temperature=float(now_data.get('temp', 0)),
                condition=now_data.get('text', '未知'),
                humidity=int(now_data.get('humidity', 0)),
                wind_speed=float(now_data.get('windSpeed', 0)),  # 和风天气直接返回km/h
                aqi=aqi,
                timestamp=datetime.utcnow()
            )
            
            # Cache the result
            try:
                self.redis_client.setex(
                    cache_key,
                    self.cache_duration,
                    json.dumps(weather_data.model_dump(), default=str)
                )
                # Also save as stale cache for fallback
                self.redis_client.set(
                    f"stale_{cache_key}",
                    json.dumps(weather_data.model_dump(), default=str)
                )
                logger.info(f"Cached weather data for {cache_key}")
            except Exception as e:
                logger.warning(f"Failed to cache weather data: {str(e)}")
            
            return weather_data
            
        except (NetworkError, APIError) as e:
            # Try to use stale cache as fallback
            logger.error(f"Weather API failed: {str(e)}. Attempting stale cache fallback...")
            
            try:
                stale_cache_key = f"stale_{cache_key}"
                stale_data = self.redis_client.get(stale_cache_key)
                
                if stale_data:
                    data = json.loads(stale_data)
                    logger.warning(f"Using stale cached weather data for {cache_key}")
                    return WeatherData(**data)
            except Exception as cache_error:
                logger.error(f"Stale cache fallback failed: {str(cache_error)}")
            
            # Return mock weather data as final fallback
            logger.warning("Using mock weather data as final fallback")
            return self._get_mock_weather()
    
    def _get_mock_weather(self) -> WeatherData:
        """Return mock weather data when API is unavailable.
        
        Returns:
            Mock weather data with reasonable defaults
        """
        return WeatherData(
            temperature=22.0,
            condition='晴',
            humidity=60,
            wind_speed=10.0,
            aqi=50,
            timestamp=datetime.utcnow()
        )
    
    def _get_air_quality(self, location: Location) -> Optional[int]:
        """Get air quality index from QWeather API.
        
        Args:
            location: Location to get AQI for
            
        Returns:
            AQI value or None if unavailable
        """
        params = {
            'key': self.qweather_key,
            'location': f"{location.longitude},{location.latitude}",
            'lang': 'zh'
        }
        
        response = safe_api_call(
            url=self.qweather_air_url,
            method='GET',
            params=params,
            timeout=10
        )
        
        result = response.json()
        
        if result.get('code') == '200':
            now_data = result.get('now', {})
            aqi_str = now_data.get('aqi')
            if aqi_str:
                return int(aqi_str)
        
        return None

    def is_weather_suitable(
        self,
        weather: WeatherData
    ) -> Tuple[bool, Optional[str]]:
        """Check if weather is suitable for walking.
        
        Args:
            weather: Weather data
            
        Returns:
            Tuple of (is_suitable, warning_message)
        """
        warnings = []
        
        # Check temperature (too hot or too cold)
        if weather.temperature > 35:
            warnings.append("气温过高，注意防暑")
        elif weather.temperature < 0:
            warnings.append("气温过低，注意保暖")
        
        # Check weather condition
        bad_conditions = ['雨', '雪', '雷', '冰雹', '暴', '台风']
        if any(cond in weather.condition for cond in bad_conditions):
            warnings.append(f"天气状况不佳：{weather.condition}")
        
        # Check wind speed (too windy, >50 km/h)
        if weather.wind_speed > 50:
            warnings.append("风力过大，不适合户外活动")
        
        # Check AQI if available
        if weather.aqi and weather.aqi > 150:
            warnings.append(f"空气质量较差 (AQI: {weather.aqi})")
        
        is_suitable = len(warnings) == 0
        warning_message = "；".join(warnings) if warnings else None
        
        return is_suitable, warning_message
    
    def monitor_weather_changes(
        self,
        location: Location,
        session_id: str
    ) -> Optional[str]:
        """Monitor weather changes during session.
        
        Args:
            location: Current location
            session_id: Session ID
            
        Returns:
            Warning message if weather deteriorates, None otherwise
        """
        # Get current weather
        current_weather = self.get_current_weather(location)
        
        # Get previous weather from session cache
        session_key = self._get_session_weather_key(session_id)
        previous_data = self.redis_client.get(session_key)
        
        if previous_data:
            previous_weather = WeatherData(**json.loads(previous_data))
            
            # Check for deterioration
            warnings = []
            
            # Temperature change
            temp_diff = abs(current_weather.temperature - previous_weather.temperature)
            if temp_diff > 5:
                warnings.append(f"温度变化较大 ({previous_weather.temperature}°C → {current_weather.temperature}°C)")
            
            # Condition worsening
            bad_conditions = ['雨', '雪', '雷', '冰雹', '暴', '台风']
            current_is_bad = any(cond in current_weather.condition for cond in bad_conditions)
            previous_is_bad = any(cond in previous_weather.condition for cond in bad_conditions)
            
            if current_is_bad and not previous_is_bad:
                warnings.append(f"天气转差：{current_weather.condition}")
            
            # Wind speed increase
            if current_weather.wind_speed > previous_weather.wind_speed + 20:
                warnings.append("风力增强")
            
            # AQI worsening
            if current_weather.aqi and previous_weather.aqi:
                if current_weather.aqi > previous_weather.aqi + 50:
                    warnings.append(f"空气质量下降 (AQI: {previous_weather.aqi} → {current_weather.aqi})")
            
            if warnings:
                warning_message = "天气变化警告：" + "；".join(warnings) + "。建议缩短路径或寻找庇护所。"
            else:
                warning_message = None
        else:
            # First check, no previous data
            warning_message = None
        
        # Update session weather cache
        self.redis_client.setex(
            session_key,
            self.cache_duration,
            json.dumps(current_weather.model_dump(), default=str)
        )
        
        return warning_message
